﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;


using GPSCrowdSource.Algorithms;

namespace GPSCrowdSource.DataStructures {
  
  /// <summary>
  /// A lightrail station.
  /// </summary>
  public class Station : GeoPosition {

    /// <summary>
    /// Mapping of the departure times for each direction at this station.
    /// </summary>
    private Dictionary<eRTDDirection,List<TimeSpan>> myDeparts = new Dictionary<eRTDDirection,List<TimeSpan>>();
    
    struct Pair {

      public Pair(Object a, Object b) : this() {A = a; B = b;}

      public Object A { get; set; }
      public Object B { get; set; }
      }

    ///// <summary>
    ///// Conclude which cardinal direction is the closest to our travel vector.
    ///// </summary>
    //private eRTDDirection GetClosestDirection(GeoPosition inA, GeoPosition inB) {
      
    //  Vector3 travel = inB-inA;

    //  Vector3 n = new Vector3(0,1,0);
    //  Vector3 e = new Vector3(1,0,0);
    //  Vector3 s = new Vector3(0,-1,0);
    //  Vector3 w = new Vector3(-1,0,0);
      
    //  float nT = n.GetAngle(travel);
    //  float eT = e.GetAngle(travel);
    //  float sT = s.GetAngle(travel);
    //  float wT = w.GetAngle(travel);
      
    //  List<Pair> l = new List<Pair>();
    //  l.Add(new Pair(eRTDDirection.N,nT));
    //  l.Add(new Pair(eRTDDirection.E,eT));
    //  l.Add(new Pair(eRTDDirection.S,sT));
    //  l.Add(new Pair(eRTDDirection.W,wT));
      
    //  Comparison<Pair> p = delegate(Pair a, Pair b) {
    //    float v1 = (float)a.B;
    //    float v2 = (float)b.B;

    //    if(v1 < v2)
    //      return -1;
    //    else if(v1 > v2)
    //      return 1;

    //    return 0;
    //    };

    //  l.Sort(p);
    //  return (eRTDDirection)l[0].A;
    //  }

    /// <summary>
    /// Create a station with the line
    /// </summary>
    /// <param name="inLine"></param>
    public Station(eRTDLine inLine, eRTDDirection inDir, String name) {
      Line = inLine;
      Direction = inDir;
      Name = name;
      Color = Color.White;
      }

    /// <summary>
    /// Get/Set the name of this station.
    /// </summary>
    public String Name { get; set; }

    /// <summary>
    /// Get/Set the direction of the line.
    /// </summary>
    public eRTDDirection Direction { get; set; }

    /// <summary>
    /// Get/Se tthe line for this station.
    /// </summary>
    public eRTDLine Line { get; set; }

    /// <summary>
    /// Get the departure times for this station going the given direction.
    /// </summary>
    public TimeSpan[] this[eRTDDirection inDir] {
      get {
        lock(myDeparts) {
          if(myDeparts.ContainsKey(inDir))
            return myDeparts[inDir].ToArray();
          return null;
          }
        }
      }
    
    /// <summary>
    /// Given two gps positions assumed to be on the same train this returns
    /// the closest departure time.
    /// </summary>
    /// <returns>null</returns>
    public Departure FindClosestDeparture(GeoPosition pos1, GeoPosition pos2) {

      lock(myDeparts) {

        // Orient gps to be in time order going forward in time such that pos1,pos2 is in time order.
        if(pos1.Time > pos2.Time) {
          GeoPosition tmp = pos1;
          pos1 = pos2;
          pos2 = tmp;
          }

        eRTDDirection foundDir= eRTDDirection.N;
        TimeSpan found = TimeSpan.MaxValue;
        TimeSpan cmp =  pos2.DayTime;
        int delta = int.MaxValue;


        foreach(eRTDDirection dir in myDeparts.Keys) {
          foreach(TimeSpan ts in myDeparts[dir]) {

            // Ignore route times that are null (not available)
            if(ts.TotalSeconds != 0 && Math.Abs((ts-cmp).TotalSeconds) < delta) {
              foundDir = dir;
              found = ts;
              delta = (int)Math.Abs((ts-cmp).TotalSeconds);
              }
            }
          }

        return new Departure(found,foundDir);
        }

      }

    /// <summary>
    /// Get/Set the color of this station.
    /// </summary>
    public Color Color { get; set; }

    /// <summary>
    /// Add a departure time.
    /// </summary>
    public void Add(eRTDDirection inDir, TimeSpan inTS) { 

      lock(myDeparts) {
        List<TimeSpan> ts;

        if(!myDeparts.ContainsKey(inDir))
          myDeparts.Add(inDir,ts = new List<TimeSpan>());
        else
          ts = myDeparts[inDir];

        if(!ts.Contains(inTS))
          ts.Add(inTS);
        }
      }
    }


  /// <summary>
  /// Keeps track of a departure and which direction it belongs to.
  /// </summary>
  public class Departure {
    
    public Departure(TimeSpan inTime, eRTDDirection inDir)  {
      Time = inTime;
      Direction = inDir;
      }

    public TimeSpan Time { get; set; }
    public eRTDDirection Direction { get; set; }
    }

  }
